package com.oracle.coherence.patterns.security.accesscontrol;

import com.oracle.coherence.patterns.security.KrbTicketPrincipal;
import com.tangosol.net.CacheFactory;

import javax.security.auth.Subject;
import java.security.Permission;
import java.security.Principal;
import java.util.Set;

/**
 * An abstract base implementation of a PermissionsChecker.
 * <p/>
 * This implementation will verify that the Subject contains a valid
 * KrbTokenPrincipal before calling the subclass method to check
 * the permissions of the KrbTokenPrincipal.
 *
 * @author Jonathan Knight
 */
public abstract class BasePermissionsChecker implements PermissionsChecker {
    // @todo try setting this to false in the extends case
    private boolean allowLocal = true;

    public BasePermissionsChecker(boolean allowLocal) {
        this.allowLocal = allowLocal;
    }

    public boolean isAllowLocal() {
        return allowLocal;
    }

    public void setAllowLocal(boolean allowLocal) {
        this.allowLocal = allowLocal;
    }

    /**
     * Check whether the specified <code>Subject</code> has authorised for
     * the requested <code>Permission</code>. If the Subject and containing
     * KrbTokenPrincipal are valid then the checkPermissionForPrincipal method
     * is called.
     * <p/>
     * If the check fails then a <code>java.lang.SecurityException</code>
     * should be thrown.
     * <p/>
     *
     * @param permission The requested permission
     * @param subject    The subject requesting permission
     * @throws SecurityException if access is not allowed
     */
    public void checkPermission(Permission permission, Subject subject) {
        if (subject == null) {
            throw new SecurityException("Missing credentials - No javax.security.auth.Subject provided");
        }

        Set<KrbTicketPrincipal> principals = subject.getPrincipals(KrbTicketPrincipal.class);
        if (principals.isEmpty()) {
            throw new SecurityException("Missing Principal - javax.security.auth.Subject does not contain a valid KrbTicketPrincipal");
        }

        KrbTicketPrincipal principal = principals.iterator().next();

        if (principal.isLocal() && !allowLocal) {
            throw new SecurityException("Missing credentials - No javax.security.auth.Subject provided");
        }

        if (!principal.isLocal()) {
            CacheFactory.log("checkAccess " + principal + " : permission=" + permission);
        }

        if (!principal.isAuthenticated()) {
            throw new SecurityException("Principal is not authenticated", principal.getLastError());
        }

        checkPermissionForPrincipal(permission, principal);
    }

    /**
     * This method should be implemented in subclasses to verify that the specified principal
     * is authorised for the specified permission.
     * <p/>
     *
     * @param permission - the requested permission
     * @param principal  - the Principal requesting permission
     */
    public abstract void checkPermissionForPrincipal(Permission permission, Principal principal);

}
